package com.cardone.platform.common.util;

import com.cardone.common.*;
import com.cardone.context.*;
import com.cardone.context.action.*;
import com.cardone.platform.common.dto.*;
import com.cardone.platform.common.service.*;
import com.cardone.platform.configuration.dto.*;
import com.cardone.platform.configuration.service.*;
import com.google.common.collect.*;
import com.mongodb.gridfs.*;
import lombok.*;
import org.apache.commons.io.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.*;
import org.springframework.data.mongodb.core.query.*;
import org.springframework.data.mongodb.gridfs.*;
import org.springframework.util.*;
import org.springframework.web.multipart.*;

import javax.servlet.http.*;
import java.io.*;
import java.net.*;
import java.util.*;

/**
 * 文件数据工具类
 *
 * @author yaohaitao
 */
public class EntityFileUtils {
    @Setter
    private static Map<String, Run2Action<HttpServletResponse, String>> writeToMap;

    static {
        EntityFileUtils.writeToMap = Maps.newHashMap();

        EntityFileUtils.writeToMap.put("xls,xlsx,doc,docx,ppt,pptx", (response, filename) -> {
            response.setContentType("application/x-msdownload");

            try {
                response.setHeader("content-disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
            } catch (final UnsupportedEncodingException e) {
                throw new DictionaryException(e);
            }
        });
    }

    private EntityFileUtils() {
    }

    /**
     * 上传
     *
     * @param request          request
     * @param uploadEntityFile 上传实体文件
     * @throws Exception
     */
    public static List<EntityFileDto> upload(final HttpServletRequest request, final EntityFileDto uploadEntityFile) throws Exception {
        final MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;

        List<MultipartFile> multipartFiles;

        // 没有指定名称时，取所有上传文件
        if (StringUtils.isBlank(uploadEntityFile.getName())) {
            multipartFiles = Lists.newArrayList(multipartRequest.getFileMap().values());
        } else {
            multipartFiles = multipartRequest.getFiles(uploadEntityFile.getName());
        }

        if (CollectionUtils.isEmpty(multipartFiles)) {
            return Lists.newArrayList();
        }

        final List<EntityFileDto> entityFileList = Lists.newArrayList();

        for (final MultipartFile multipartFile : multipartFiles) {
            final EntityFileDto saveEntityFile = EntityFileDto.newEntityFile();

            BeanUtils.copyProperties(uploadEntityFile, saveEntityFile);

            saveEntityFile.setName(multipartFile.getName());

            saveEntityFile.setOriginalFilename(multipartFile.getOriginalFilename());

            saveEntityFile.setExtension(StringUtils.lowerCase(FilenameUtils.getExtension(saveEntityFile.getOriginalFilename())));

            saveEntityFile.setFilename(UUID.randomUUID().toString() + Characters.dot.stringValue() + saveEntityFile.getExtension());

            byte[] buff = multipartFile.getBytes();

            try (ByteArrayInputStream fileIs = new ByteArrayInputStream(buff)) {
                EntityFileUtils.saveFile(fileIs, saveEntityFile.getFilename(), multipartFile.getContentType());
            }

            saveEntityFile.setContentType(multipartFile.getContentType());

            EntityFileDto entityFile = ContextHolder.getBean(EntityFileService.class).saveByIdOrCode(EntityFileDto.class, saveEntityFile);

            entityFile.setThumbnailsTypeCode(saveEntityFile.getThumbnailsTypeCode());
            entityFile.setThumbnailsCode(saveEntityFile.getThumbnailsCode());
            entityFile.setName(saveEntityFile.getName());

            entityFileList.add(entityFile);

            EntityFileUtils.buildThumbnails(entityFile, buff);
        }

        return entityFileList;
    }

    /**
     * 保存文件
     *
     * @param inputStream 多文件
     * @param filename
     * @throws IOException
     */
    private static void saveFile(final InputStream inputStream, final String filename, String contentType) throws IOException {
        ContextHolder.getBean(GridFsTemplate.class).delete(new org.springframework.data.mongodb.core.query.Query(org.springframework.data.mongodb.core.query.Criteria.where("filename").is(filename)));

        ContextHolder.getBean(GridFsTemplate.class).store(inputStream, filename, contentType).save();
    }

    /**
     * 生成缩略图
     *
     * @param bigEntityFile 保存实体文件
     * @param buff          multipartFile
     * @throws IOException
     */
    private static void buildThumbnails(final com.cardone.platform.common.dto.EntityFileDto bigEntityFile, byte[] buff) throws IOException {
        if (StringUtils.isBlank(bigEntityFile.getThumbnailsTypeCode())) {
            return;
        }

        final String images = ContextHolder.getBean(DictionaryService.class).readValueByTypeCodeAndCode("jpg,png,gif", "file_type", "image");

        if (!StringUtils.contains(images, bigEntityFile.getExtension())) {
            return;
        }

        final List<String> whList = EntityFileUtils.findListWhList(bigEntityFile);

        if (CollectionUtils.isEmpty(whList)) {
            return;
        }

        for (final String wh : whList) {
            final String[] whs = StringUtils.split(wh, Characters.x.stringValue());

            if (ArrayUtils.getLength(whs) < 2) {
                continue;
            }

            final int width = NumberUtils.toInt(whs[0], 160);

            final int height = NumberUtils.toInt(whs[1], 160);

            String fileBaseName = FilenameUtils.getBaseName(bigEntityFile.getFilename());

            String newWH = width + Characters.x.stringValue() + height;

            final String newFilename = fileBaseName + Characters.underline.stringValue() + newWH + Characters.dot.stringValue() + bigEntityFile.getExtension();

            try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                try (ByteArrayInputStream fileIs = new ByteArrayInputStream(buff)) {
                    net.coobird.thumbnailator.Thumbnails.of(fileIs).size(width, height).keepAspectRatio(false).toOutputStream(outputStream);
                }

                try (InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray())) {
                    EntityFileUtils.saveFile(inputStream, newFilename, bigEntityFile.getContentType());
                }

                outputStream.flush();
            }

            final EntityFileDto saveThumbnailsEntityFile = EntityFileDto.newEntityFile();

            BeanUtils.copyProperties(bigEntityFile, saveThumbnailsEntityFile);

            saveThumbnailsEntityFile.setId(null);

            saveThumbnailsEntityFile.setFilename(newFilename);

            saveThumbnailsEntityFile.setThumbnailValue(newWH);

            saveThumbnailsEntityFile.setParentId(bigEntityFile.getId());

            if (org.apache.commons.collections.CollectionUtils.isEmpty(bigEntityFile.getChilds())) {
                bigEntityFile.setChilds(Lists.newArrayList());
            }

            EntityFileDto entityFile = ContextHolder.getBean(EntityFileService.class).saveByIdOrCode(EntityFileDto.class, saveThumbnailsEntityFile);

            bigEntityFile.getChilds().add(entityFile);
        }
    }

    /**
     * 查询宽高集合
     *
     * @param saveEntityFile 实体文件对象
     * @return 宽高集合
     */
    private static List<String> findListWhList(final EntityFileDto saveEntityFile) {
        final List<String> whList = Lists.newArrayList();

        if (StringUtils.isNotBlank(saveEntityFile.getThumbnailsCode())) {
            final String wh = ContextHolder.getBean(DictionaryService.class).readValueByTypeCodeAndCode(saveEntityFile.getThumbnailsTypeCode(), saveEntityFile.getThumbnailsCode());

            if (StringUtils.isBlank(wh)) {
                return whList;
            }

            whList.add(wh);

            return whList;
        }

        final List<DictionaryDto> thumbnailsList = ContextHolder.getBean(DictionaryService.class).findListByTypeCode(DictionaryDto.class, saveEntityFile.getThumbnailsTypeCode());

        if (CollectionUtils.isEmpty(thumbnailsList)) {
            return whList;
        }

        for (final DictionaryDto thumbnails : thumbnailsList) {
            whList.add(thumbnails.getValue());
        }

        return whList;
    }

    /**
     * 写入文件
     *
     * @param toOutputStream
     * @param filename
     * @throws IOException
     */
    public static void writeTo(final OutputStream toOutputStream, final String filename) throws IOException {
        final GridFSDBFile gridFSDBFile = EntityFileUtils.findOneGridFSDBFile(filename);

        if (gridFSDBFile == null) {
            return;
        }

        gridFSDBFile.writeTo(toOutputStream);
    }

    /**
     * 查询文件
     *
     * @param filename 文件名
     * @return GridFSDBFile
     */
    private static GridFSDBFile findOneGridFSDBFile(final String filename) {
        final GridFSDBFile gridFSDBFile = ContextHolder.getBean(GridFsTemplate.class).findOne(new Query(Criteria.where("filename").is(filename)));

        return gridFSDBFile;
    }

    /**
     * 保存文件
     *
     * @param inputStream 多文件
     * @param filename
     * @throws IOException
     */
    private static void saveFile(final InputStream inputStream, final String filename) throws IOException {
        saveFile(inputStream, filename, null);
    }

    /**
     * 写入流
     *
     * @param response
     * @param filename 文件名
     * @throws IOException
     */
    public static void writeTo(final HttpServletResponse response, final String filename) throws IOException {
        final GridFSDBFile gridFSDBFile = EntityFileUtils.findOneGridFSDBFile(filename);

        if (gridFSDBFile == null) {
            return;
        }

        response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");

        final String fileExtension = FilenameUtils.getExtension(filename);

        for (final String writeToKey : EntityFileUtils.writeToMap.keySet()) {
            if (StringUtils.containsIgnoreCase(writeToKey, fileExtension)) {
                EntityFileUtils.writeToMap.get(writeToKey).run(response, filename);

                try (OutputStream out = response.getOutputStream()) {
                    gridFSDBFile.writeTo(out);

                    out.flush();

                    response.flushBuffer();
                }

                return;
            }
        }

        response.setContentType(gridFSDBFile.getContentType());

        try (OutputStream out = response.getOutputStream()) {
            gridFSDBFile.writeTo(out);

            out.flush();

            response.flushBuffer();
        }
    }

    /**
     * 写入流
     *
     * @param toFilename
     * @param filename
     * @throws IOException
     */
    public static void writeTo(final String toFilename, final String filename) throws IOException {
        writeTo(new File(toFilename), filename);
    }

    /**
     * 写入文件
     *
     * @param toFile
     * @param filename
     * @throws IOException
     */
    public static void writeTo(final File toFile, final String filename) throws IOException {
        final GridFSDBFile gridFSDBFile = EntityFileUtils.findOneGridFSDBFile(filename);

        if (gridFSDBFile == null) {
            return;
        }

        if (toFile.exists()) {
            org.apache.commons.io.FileUtils.deleteQuietly(toFile);
        }

        gridFSDBFile.writeTo(toFile);
    }
}
